import 'dart:convert';

import 'package:dio/dio.dart';
import '../../core/app_config.dart';
import '../../core/auth/user_manager.dart';
import '../../core/crypto/hmac_utils.dart';
import '../../exception/auth_exceptions.dart';
import '../../exception/exception_model.dart';
import '../../exception/global_error_handler.dart';

class RestApiInterceptor extends Interceptor {
  final UserManager um = UserManager();

  static const String refreshTokenPath = '/api/auth/refreshToken';

  @override
  void onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    // 这里需要特殊处理，当请求时refresh_token接口时，不要添加Authorization头
    var isRefreshToken = options.path.endsWith(refreshTokenPath);
    if (!isRefreshToken) {
      // 获取有效的访问令牌(内部可自动刷新)
      final accessToken = await um.getValidAccessToken();
      if (accessToken != null && accessToken.isNotEmpty) {
        options.headers['Authorization'] = 'Bearer $accessToken';
      }
    }

    // multipart/form-data不进行HMAC签名
    final contentType = options.headers['Content-Type'] ?? '';
    if (contentType.startsWith('multipart/form-data')) {
      return handler.next(options);
    }

    // HMAC 签名
    final timestamp = HmacUtil.generateTimestamp();
    final nonce = HmacUtil.generateNonce();
    String bodyHash = '';
    if (options.data != null) {
      var jsonBody = jsonEncode(options.data);
      bodyHash = HmacUtil.hashBody(jsonBody);
    }

    // 生成签名
    final signature = HmacUtil.generateSignature(
      options.method,
      options.path,
      options.queryParameters.isNotEmpty
          ? Uri(queryParameters: options.queryParameters).query
          : '',
      bodyHash,
      timestamp,
      nonce,
      AuthCenterConfig.clientSecret,
    );

    options.headers.addAll({
      'X-Client-Id': AuthCenterConfig.clientId,
      'X-Timestamp': timestamp,
      'X-Nonce': nonce,
      'X-Signature': signature,
    });

    handler.next(options);
  }

  @override
  onResponse(Response response, ResponseInterceptorHandler handler) {
    final data = response.data;

    // 先做基本结构判断
    if (data is! Map<String, dynamic>) {
      return handler.next(response);
    }

    final code = data['code'];
    final message = data['message'] ?? '';
    final success = code == 0 || code == 200;

    if (!success) {
      // 业务失败，转成 DioException 交给 onError 统一处理
      throw DioException(
        requestOptions: response.requestOptions,
        type: DioExceptionType.badResponse,
        error: AppException(
          code: code,
          message: message ?? '业务错误',
        ),
        response: response,
      );
    }

    // 业务成功，继续
    handler.next(response);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) async {
    final res = err.response;
    final req = err.requestOptions;
    // 防止循环重试
    final retried = (req.headers['X-Retried'] == '1');

    if (res?.statusCode == 400 && res?.data != null) {
      GlobalErrorHandler().handleError(
          AuthException.badRequest(res?.data['message'] ?? '请求失败，请联系管理员工'));
      return handler.reject(err);
    }

    // 如果是 401，尝试刷新 Token 并重试
    if (res?.statusCode == 401 && !retried) {
      // 如果是刷新令牌接口本身返回 401，说明刷新失败，直接清除并跳转登录
      if (req.path.endsWith(refreshTokenPath)) {
        GlobalErrorHandler()
            .handleError(AuthException.unauthorized("登录信息失效，请重新登录"));
        return handler.next(err);
      }

      final refreshed = await um.refreshToken();
      if (refreshed) {
        // 刷新成功 → 重新发起原请求
        final requestOptions = err.requestOptions;
        requestOptions.headers['X-Retried'] = '1';
        final dio = Dio();
        final newResponse = await dio.fetch(requestOptions);
        return handler.resolve(newResponse);
      } else {
        // 刷新失败 → 清除并跳转登录
        GlobalErrorHandler()
            .handleError(AuthException.unauthorized("登录信息失效，请重新登录"));
        return handler.next(err);
      }
    }

    if (res?.statusCode == 403) {
      GlobalErrorHandler().handleError(AuthException.forbidden('无权限访问，请联系管理员'));
      return handler.next(err);
    }

    // 其他错误
    GlobalErrorHandler().handleError(err.error ?? err);
    handler.next(err);
  }
}
